package com.wingjay.blurimageviewlib;

import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.assist.QueueProcessingType;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
import ohos.agp.components.*;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.global.resource.Resource;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This imageView can show blur image.
 * Then it will be expanded to automatically display one image with two styles
 * one is small and blurry, another is the origin image,So here are two urls for these two images.
 */
public class BlurImageView extends DependentLayout {

    /**
     * DEFAULT_BLUR_FACTOR
     */
    public final static int DEFAULT_BLUR_FACTOR = 8;
    private final Context mContext;
    private int mBlurFactor = DEFAULT_BLUR_FACTOR;
    private String mBlurImageUrl;
    private String mOriginImageUrl;
    private final int greyColor = Color.getIntColor("#66CCCCCC");
    private Element defaultDrawable = new ShapeElement() {
        @Override
        public void drawToCanvas(Canvas canvas) {
            canvas.drawColor(greyColor, Canvas.PorterDuffMode.CLEAR);
        }
    };
    private Element failDrawable = new ShapeElement() {
        final Paint textPaint = new Paint();

        @Override
        public void drawToCanvas(Canvas canvas) {
            textPaint.setAntiAlias(true);
            canvas.drawColor(greyColor, Canvas.PorterDuffMode.CLEAR);
            String failString = "load failure";
            canvas.translate((getHeight() - textPaint.measureText(failString)) / 2,
                    getWidth() * 1.0f / 2);
            textPaint.setColor(Color.DKGRAY);
            textPaint.setTextSize(30);
            canvas.drawText(textPaint, failString, 0, failString.length());
        }
    };

    private ImageLoader imageLoader;
    private DisplayImageOptions displayImageOptions;
    private Image imageView;
    private LoadingCircleProgressView loadingCircleProgressView;
    private boolean enableProgress = true;

    public BlurImageView(Context context) {
        this(context, null);
    }

    public BlurImageView(Context context, AttrSet attrs) {
        this(context, attrs, null);
    }

    public BlurImageView(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context.getApplicationContext();
        init();
    }

    private void init() {
        initUIL();
        imageLoader = ImageLoader.getInstance();

        initChildView();
        initDisplayImageOptions();
    }

    private void initUIL() {
        ImageLoaderConfiguration.Builder config = new ImageLoaderConfiguration.Builder(mContext);
        config.threadPriority(Thread.NORM_PRIORITY - 2);
        config.denyCacheImageMultipleSizesInMemory();
        config.diskCacheFileNameGenerator(new Md5FileNameGenerator());
        config.diskCacheSize(50 * 1024 * 1024); // 50 MiB
        config.tasksProcessingOrder(QueueProcessingType.LIFO);
        config.writeDebugLogs(); // Remove for release app

        // Initialize ImageLoader with configuration.
        ImageLoader.getInstance().init(config.build());
    }

    private void initDisplayImageOptions() {
        displayImageOptions = new DisplayImageOptions.Builder()
                .cacheOnDisk(false)
                .considerExifParams(true)
                .bitmapConfig(PixelFormat.RGB_565)
                .build();
    }

    private void initChildView() {
        imageView = new Image(mContext);
        imageView.setLayoutConfig(new ComponentContainer.LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT));
        imageView.setScaleMode(Image.ScaleMode.CLIP_CENTER);
        imageView.setImageElement(defaultDrawable);

        loadingCircleProgressView = new LoadingCircleProgressView(mContext);
        DependentLayout.LayoutConfig layoutConfig = new DependentLayout.LayoutConfig(LayoutConfig.MATCH_CONTENT, LayoutConfig.MATCH_CONTENT);
        layoutConfig.addRule(LayoutConfig.CENTER_IN_PARENT);
        loadingCircleProgressView.setLayoutConfig(layoutConfig);
        loadingCircleProgressView.setVisibility(Component.HIDE);

        addComponent(imageView);
    }

    private final SimpleImageLoadingListener blurLoadingListener = new SimpleImageLoadingListener() {
        @Override
        public void onLoadingFailed(String imageUri, Component view, FailReason failReason) {
            imageView.setImageElement(failDrawable);
        }

        @Override
        public void onLoadingComplete(String imageUri, Component view, PixelMap loadedImage) {
            imageView.setPixelMap(getBlurBitmap(loadedImage));
        }
    };

    private final SimpleImageLoadingListener fullLoadingListener = new SimpleImageLoadingListener() {
        @Override
        public void onLoadingFailed(String imageUri, Component view, FailReason failReason) {
            imageView.setImageElement(failDrawable);
            Logger.getLogger(BlurImageView.class.getName()).log(Level.INFO, "Image Load error", "cannot load Small image, please check url or network status");
        }

        @Override
        public void onLoadingComplete(String imageUri, Component view, PixelMap loadedImage) {
            imageView.setPixelMap(getBlurBitmap(loadedImage));
            imageLoader.displayImage(mOriginImageUrl, imageView, displayImageOptions,
                    new ImageLoadingListener() {
                        @Override
                        public void onLoadingStarted(String imageUri, Component view) {
                            setLoadingProgressRatio(5, 100);
                        }

                        @Override
                        public void onLoadingFailed(String imageUri, Component view, FailReason failReason) {
                            setLoadingProgressRatio(5, 100);
                        }

                        @Override
                        public void onLoadingComplete(String imageUri, Component view, PixelMap loadedImage) {
                            setLoadingProgressRatio(100, 100);
                        }

                        @Override
                        public void onLoadingCancelled(String imageUri, Component view) {
                            setLoadingProgressRatio(100, 100);
                        }
                    },
                    (imageUri1, view1, current, total) -> {
                        if (!enableProgress) {
                            return;
                        }
                        setLoadingProgressRatio(current, total);
                    });
        }
    };

    private void setLoadingProgressRatio(int current, int total) {
        if (current < total) {
            if (loadingCircleProgressView.getVisibility() == Component.HIDE) {
                loadingCircleProgressView.setVisibility(VISIBLE);
            }
            loadingCircleProgressView.setCurrentProgressRatio((float) current / total);
        } else {
            loadingCircleProgressView.setVisibility(Component.HIDE);
        }
    }

    /**
     * This method will fetch bitmap from resource and make it blurry, display
     *
     * @param blurImageRes the image resource id which is needed to be blurry
     */
    public void setBlurImageByRes(int blurImageRes, Context context) {
        PixelMap blurBitmap = FastBlurUtil.doBlur(getPixelMapByRes(blurImageRes), mBlurFactor, true);
        imageView.setPixelMap(blurBitmap);
        if (context != null) {
            context.getUITaskDispatcher().delayDispatch(() -> imageView.setPixelMap(getPixelMapByRes(blurImageRes)), 300);
        }
    }

    private PixelMap getPixelMapByRes(int blurImageRes) {
        try {
            Resource resource = getContext().getResourceManager().getResource(blurImageRes);
            ImageSource imageSource = ImageSource.create(resource, new ImageSource.SourceOptions());
            ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
            return imageSource.createPixelmap(decodingOptions);
        } catch (IOException | NotExistException e) {
            Logger.getLogger(BlurImageView.class.getName(), e.getMessage());
            return null;
        }
    }

    /**
     * This image won't be blurry.
     *
     * @param originImageRes The origin image resource id.
     */
    public void setOriginImageByRes(int originImageRes) {
        try {
            Resource resource = getContext().getResourceManager().getResource(originImageRes);
            ImageSource imageSource = ImageSource.create(resource, new ImageSource.SourceOptions());
            ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
            PixelMap pixelmap = imageSource.createPixelmap(decodingOptions);
            imageView.setPixelMap(pixelmap);
        } catch (Exception e) {
            Logger.getLogger(BlurImageView.class.getName(), e.getMessage());
        }
    }

    /**
     * setBlurImageByUrl
     *
     * @param blurImageUrl url
     */
    public void setBlurImageByUrl(String blurImageUrl) {
        mBlurImageUrl = blurImageUrl;
        cancelImageRequestForSafety();
        imageLoader.loadImage(blurImageUrl, blurLoadingListener);
    }

    /**
     * setting originImageUrl
     *
     * @param originImageUrl url
     */
    public void setOriginImageByUrl(String originImageUrl) {
        mOriginImageUrl = originImageUrl;
        imageLoader.displayImage(originImageUrl, imageView);
    }

    /**
     * This will load two Images literally. The small size blurry one and the big size original one.
     *
     * @param blurImageUrl   This is a small image url and will be loaded fast and will be blurry
     *                       automatically.
     * @param originImageUrl After show the blurry image, it will load the origin image automatically
     *                       and replace the blurry one after finish loading.
     */
    public void setFullImageByUrl(String blurImageUrl, String originImageUrl) {
        mBlurImageUrl = blurImageUrl;
        mOriginImageUrl = originImageUrl;
        cancelImageRequestForSafety();
        imageLoader.loadImage(blurImageUrl, displayImageOptions, fullLoadingListener);
    }

    private PixelMap getBlurBitmap(PixelMap loadedBitmap) {

        return FastBlurUtil.doBlur(loadedBitmap, getBlurFactor(), true);
    }

    private int getBlurFactor() {
        return mBlurFactor;
    }

    /**
     * setBlurFactor
     *
     * @param blurFactor Factor
     */
    public void setBlurFactor(int blurFactor) {
        if (blurFactor < 0) {
            throw new IllegalArgumentException("blurFactor must not be less than 0");
        }
        mBlurFactor = blurFactor;
    }

    /**
     * cancelImageRequestForSafety
     */
    public void cancelImageRequestForSafety() {
        imageLoader.cancelDisplayTask(imageView);
    }

    /**
     * clear
     */
    public void clear() {
        cancelImageRequestForSafety();
        imageView.setPixelMap(null);
    }

    /**
     * If you disable progress, then it won't show a loading progress view when you're loading image.
     * Default the progress view is enabled.
     */
    public void disableProgress() {
        this.enableProgress = false;
    }

    /**
     * setProgressBarBgColor
     *
     * @param bgColor bgColor
     */
    public void setProgressBarBgColor(int bgColor) {
        this.loadingCircleProgressView.setProgressBgColor(bgColor);
    }

    /**
     * setProgressBarColor
     *
     * @param color color
     */
    public void setProgressBarColor(int color) {
        this.loadingCircleProgressView.setProgressColor(color);
    }

    /**
     * setFailDrawable
     *
     * @param failDrawable failDrawable
     */
    public void setFailDrawable(Element failDrawable) {
        this.failDrawable = failDrawable;
    }

    /**
     * setDefaultDrawable
     *
     * @param defaultDrawable defaultDrawable
     */
    public void setDefaultDrawable(Element defaultDrawable) {
        this.defaultDrawable = defaultDrawable;
    }
}
